package org.jeecg.modules.xindian.reconciliation.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jeecg.modules.xindian.reconciliation.controller.enums.ChannelSettlementConfigState;
import org.jeecg.modules.xindian.reconciliation.controller.req.PolicyRelationExcel;
import org.jeecg.modules.xindian.reconciliation.entity.*;
import org.jeecg.modules.xindian.reconciliation.enums.RelationType;
import org.jeecg.modules.xindian.reconciliation.enums.SettlementState;
import org.jeecg.modules.xindian.reconciliation.enums.SettlementType;
import org.jeecg.modules.xindian.reconciliation.mapper.InsuranceSettlementInfoMapper;
import org.jeecg.modules.xindian.reconciliation.mapper.PolicySettlementTypeStatisticsMapper;
import org.jeecg.modules.xindian.reconciliation.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static org.jeecg.modules.xindian.reconciliation.service.IInsuranceSettlementConfigService.ALL_AREA;

/**
 * @Description: 保险公司结算汇总
 * @Author: jeecg-boot
 * @Date: 2024-04-11
 * @Version: V1.0
 */
@Service
@Slf4j
public class InsuranceSettlementInfoServiceImpl extends ServiceImpl<InsuranceSettlementInfoMapper, InsuranceSettlementInfo> implements IInsuranceSettlementInfoService {
    @Autowired
    private IInsuranceSettlementConfigService configService;
    @Autowired
    private InsuranceStatisticsServiceImpl insuranceStatisticsService;
    @Autowired
    private IPolicySettlementTypeStatisticsService policySettlementTypeStatisticsService;
    @Autowired
    private PolicySettlementTypeStatisticsMapper policySettlementTypeStatisticsMapper;
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    public void loadSettlementByUpLoadFile(String id, List<PolicyRelationExcel> relationExcels) {
        InsuranceSettlementInfo settlementInfo = getById(id);
        Map<String,PolicyRelationExcel> relationMaps = relationExcels.stream().collect(Collectors.toMap(PolicyRelationExcel::getPolicyNo, Functions.identity()));
        List<PolicySettlementTypeStatistics> processData = new ArrayList<>();
        if (CollectionUtil.isEmpty(relationExcels)) {
            settlementInfo.setInsuranceAmt(BigDecimal.ZERO);
            settlementInfo.setBenefit(BigDecimal.ZERO);
            settlementInfo.setInsuranceNum(0);
            transactionTemplate.execute(status -> {
                //清理历史数据
                policySettlementTypeStatisticsMapper.cleanInsuranceSettlementInfo(id);
                updateById(settlementInfo);
                return null;
            });
        } else {
            InsuranceSettlementConfig config = configService.loadByCompany(settlementInfo.getCompany());
            Assert.notNull(config,"结算单位{}未找到配置信息",config);
            config.buildConfig();
            Assert.notEmpty(config.getConfigs(), "结算单位{}保费配置信息未配置", settlementInfo.getCompany());
            //加载保单数据
            List<String> policyNo =relationExcels.stream().map(PolicyRelationExcel::getPolicyNo).collect(Collectors.toList());
            List<InsuranceStatistics> statistics = insuranceStatisticsService.lambdaQuery().in(InsuranceStatistics::getPolicyNo, policyNo).list();
            List<String> ids = new ArrayList<>(policyNo);
            List<String> orders = statistics.stream().map(InsuranceStatistics::getPolicyNo).collect(Collectors.toList());
            ids.removeAll(orders);
            Assert.isTrue(CollectionUtils.isEmpty(ids), "统计数据中未找到以下保单数据,请核对", StringUtils.collectionToDelimitedString(ids, ","));
            List<PolicySettlementTypeStatistics> policySettlementStatistics = statistics.stream()
                    .map(sta ->{
                        PolicyRelationExcel excel =    relationMaps.get(sta.getPolicyNo());
                        Assert.notNull(excel,"保单{}未配置费率信息",sta.getPolicyNo());
                        BigDecimal rate = excel.getRate();
                        PolicySettlementTypeStatistics  one =   policySettlementTypeStatisticsService.build(sta, id, RelationType.INSURANCE, rate);
                        one.setDate(settlementInfo.getDate());
                        one.setCompany(settlementInfo.getCompany());
                        return one;
                    })
                    .collect(Collectors.toList());
            processData.addAll(policySettlementStatistics);
            //统计汇总数据
            computerTotal(settlementInfo, processData);
            transactionTemplate.execute(status -> {
                //清理历史数据
                policySettlementTypeStatisticsService.process(processData, id,settlementInfo.getDate() ,RelationType.INSURANCE);
                updateById(settlementInfo);
                return null;
            });
        }
    }


    private void computerTotal(InsuranceSettlementInfo settlementInfo, List<PolicySettlementTypeStatistics> relations) {
        settlementInfo.setInsuranceNum(relations.size());
        final BigDecimal[] insuranceAmt = {BigDecimal.ZERO};
        final BigDecimal[] benefit = {BigDecimal.ZERO};
        relations.forEach(relation -> {
            BigDecimal amt = insuranceAmt[0].add(relation.getCost());
            BigDecimal ft = benefit[0].add(relation.getInsuranceFee());
            insuranceAmt[0] = amt;
            benefit[0] = ft;
        });
        settlementInfo.setState(SettlementState.CHECKING);
        settlementInfo.setInsuranceAmt(insuranceAmt[0]);
        settlementInfo.setBenefit(benefit[0]);
    }


    /**
     * 更新信息
     *
     * @param config
     */
    private BigDecimal findRate(InsuranceSettlementConfig.SettlementConfig config, BigDecimal totalValue) {
        SettlementType settlementType = config.getType();
        BigDecimal rate = BigDecimal.ZERO;
        switch (settlementType) {
            case SOLITARY: {
                rate = config.getFixRate();
            }
            break;
            case LEVEL: {
                //查找阶梯信息
                InsuranceSettlementConfig.TrendsRateConfig trendsRateConfig = config.getTrendsRateConfig();
                log.info("保费总金额{},阶梯信息配置{}", totalValue, trendsRateConfig);
                if (totalValue.compareTo(trendsRateConfig.getMin().getValue()) <= 0) {
                    rate = trendsRateConfig.getMin().getRate();
                    log.info("保费总金额{},阶梯信息配置{}", totalValue, trendsRateConfig);
                } else if (totalValue.compareTo(trendsRateConfig.getMax().getValue()) >= 0) {
                    rate = trendsRateConfig.getMax().getRate();
                    log.info("保费总金额{},阶梯信息配置{}", totalValue, trendsRateConfig);
                } else {
                    List<InsuranceSettlementConfig.BT> bts = trendsRateConfig.getInterval();
                    InsuranceSettlementConfig.BT bt = findBTRate(bts, totalValue);
                    Assert.notNull(bt, "费率阶梯保费总金额{}配置信息{}查询失败",totalValue, JSON.toJSON(trendsRateConfig));
                    log.info("保费总金额{},阶梯信息配置{}", totalValue, trendsRateConfig);
                }
            }
            break;
        }
        return rate;
    }

    private InsuranceSettlementConfig.BT findBTRate(List<InsuranceSettlementConfig.BT> vals, BigDecimal premium) {
        return vals.stream().filter(bt -> bt.getLgt().compareTo(premium) <= 0 && bt.getLt().compareTo(premium) > 0).findFirst().orElseGet(null);
    }

    @Override
    public void synchronizeLastMonth() {
//        Date lastMounth = DateUtil.parseDate("2024-03-02");
        Date lastMounth = DateUtil.offsetMonth(new Date(), -1);
        synchronizeMonth(lastMounth);

    }

    @Override
    public void synchronizeMonth(Date date) {
        String lastMounthString = DateUtil.format(date, "yyyy-MM");
        //同步上个月的状态
        List<InsuranceSettlementConfig> insuranceSettlementConfigs = configService.queryEnableConfig();
        //构建主记录
        List<InsuranceSettlementInfo> addSettlementInfo = new ArrayList<>();
        List<PolicySettlementTypeStatistics> addSettlementPolicyRelation = new ArrayList<>();
        for (InsuranceSettlementConfig cof : insuranceSettlementConfigs) {
            List<PolicySettlementTypeStatistics> addGroup = new ArrayList<>();
            InsuranceSettlementInfo info = new InsuranceSettlementInfo();
            info.setId(IdUtil.fastUUID());
            info.setCompany(cof.getCompany());
            info.setBenefit(BigDecimal.ZERO);
            info.setDate(lastMounthString);
            info.setInsuranceAmt(BigDecimal.ZERO);
            info.setInsuranceNum(0);
            info.setState(SettlementState.CHECKING);
            info.setType(cof.getRule());
            Long numbers = lambdaQuery().eq(InsuranceSettlementInfo::getCompany, cof.getCompany()).eq(InsuranceSettlementInfo::getDate, lastMounthString).count();
            Assert.isTrue(numbers == 0, "保险公司{}账单{}已被初始化过,不能重复加载", cof.getInsurance(), lastMounthString);
            List<InsuranceSettlementConfig.SettlementConfig> configs = cof.buildConfig();
            for (InsuranceSettlementConfig.SettlementConfig oneConfig : configs) {
                List<String> platforms = oneConfig.getPlatform();
                log.info("当前保险公司{},配置平台如下{}", cof.getInsurance(), StringUtils.collectionToCommaDelimitedString(platforms));
                List<InsuranceStatistics> dt = insuranceStatisticsService.loadByInsuranceAndPlatform(cof.getInsurance(), platforms, cof.getRule().equals(ChannelSettlementConfigState.PAY), date);
                if (CollectionUtils.isEmpty(dt)) {
                    continue;
                }
                BigDecimal rate = findRate(oneConfig, totalCost(dt));
                List<PolicySettlementTypeStatistics> relations = dt.stream().map(static1 -> {
                    PolicySettlementTypeStatistics policySettlementTypeStatistics = policySettlementTypeStatisticsService.build(static1, info.getId(), RelationType.INSURANCE, rate);
                    policySettlementTypeStatistics.setDate(lastMounthString);
                    policySettlementTypeStatistics.setCompany(cof.getCompany());
                    return policySettlementTypeStatistics;
                }).collect(Collectors.toList());
                addGroup.addAll(relations);
            }
            if (CollectionUtils.isEmpty(addGroup)) {
                continue;
            }
            addSettlementInfo.add(info);
            //这里需要过滤之前数据库存在的数据
            Map<String, PolicySettlementTypeStatistics> fromDBs = policySettlementTypeStatisticsService.queryByPolicyNos(addGroup.stream().map(PolicySettlementTypeStatistics::getPolicyNo)
                    .collect(Collectors.toList()));
           List<PolicySettlementTypeStatistics> filterAddGroup =  addGroup.stream()
                   .filter((Predicate<PolicySettlementTypeStatistics>) policySettlementTypeStatistics -> fromDBs.get(policySettlementTypeStatistics.getPolicyNo())==null)
                   .collect(Collectors.toList());
            computerTotal(info, filterAddGroup);
            addSettlementPolicyRelation.addAll(filterAddGroup);
        }

        transactionTemplate.execute(status -> {
            saveBatch(addSettlementInfo);
            policySettlementTypeStatisticsService.saveBatch(addSettlementPolicyRelation);
            return null;
        });
    }

    private BigDecimal totalCost(List<InsuranceStatistics> statistics) {
        BigDecimal total = BigDecimal.ZERO;
        for (InsuranceStatistics statistic : statistics) {
            total = total.add(statistic.getCost());
        }
        return total;
    }

    @Override
    public void confirmPolicy(String id) {
        InsuranceSettlementInfo settlementInfo = getById(id);
        Assert.isTrue(settlementInfo.getState().equals(SettlementState.CHECKING), "当前保单状态不是待确定状态,无法确定");
        settlementInfo.setState(SettlementState.CHECKED);
        updateById(settlementInfo);
    }
}
